﻿using ComData;
using Modbus.Device;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using VFTray.Config;

namespace VFTray
{
	class FeedTray
	{
		#region Camera

		internal MDVCamera.MdvCamera2 Cam = new MDVCamera.MdvCamera2();
		internal void Init_Cam(MDVCamera.MdvParam cfgCamera)
		{
			Cam.OnOpened += Cam_Opened;
			Cam.OnSnapped += Cam_OnSnaped;

			Cam.Param = cfgCamera; //相机参数
			Cam.SaveImageO = GlobalParams.CD.SaveImageO; //是否保存原图
			Cam.PathImageO = GlobalParams.CD.ImagesPathO; //保存路径
		}

		private void Cam_OnSnaped(object sender, EventArgs e)
		{
			if(wndCfg!=null)
			{
				wndCfg.SetPicture(Cam.mBitmap);
			}
			GlobalParams.MF.wndRun.SetPicture(ID,Cam.mBitmap);
			GlobalParams.MF.wndRun.AddMessage(ID, "Snap");

			if (snap_steps.Runing)
			{
				GlobalParams.MF.wndRun.AddMessage(ID, "拍照成功");
				int mark = snap_steps.Current.mark;
				var vp = GetVision(mark);
				int count_req = snap_steps.Current.count_req;
				if (GetStep().req_mode == 1)
				{
					count_req = -1;//greedy mode
				}

				//CV.SetCalibrationPts(GetCalibrationPts());
				CV.SetBackParams(vp.method, vp.binm, vp.binp);
				CV.SetBackMarkFn(vp.mark_fn);

				//CV.SetImageWHCh(Cam.Param.GetW(), Cam.Param.GetH(), 1);
				CV.SetROI(vp.roi_x, vp.roi_y, vp.roi_w, vp.roi_h);
				CV.SetFlip(GetCfgFeed().Flip, GetCfgFeed().Trans);

				CV.SetImageBack(Cam.Data);

				Task.Run(() =>
				{
					if (vp.method == 0 || vp.method == 1)
					{
						CV.ProcessBackMark(vp.method);
						if (CV.ProcessBack(vp.method, vp.tolerancePer, count_req) > 0)
						{
							CV.GetResultItem(mark);
						}
					}
					else if (vp.method == 2)
					{
						string di = Path.Combine(GlobalParams.CD.ImagesPathO, GetCfgFeed().cfgCamera.Name);
						CV.SetYamlPath(di);
						CV.SetTemplateParam(vp.method, vp.matchParam.num_features,
							vp.matchParam.degree_begin, vp.matchParam.degree_end, vp.matchParam.step_degree,
							vp.matchParam.pyramid, vp.matchParam.weak_threshold, vp.matchParam.strong_threshold);

						System.Diagnostics.Debug.WriteLine("MATCH requireCount = " + vp.matchParam.requireCount.ToString());
						if (CV.processMatch(vp.method, vp.matchParam.similarity, vp.matchParam.requireCount,vp.focusX,vp.focusY) > 0)
						{
							CV.GetMatchData(mark);
						}
					}

					snap_steps.Result = true;
					GlobalParams.MF.wndRun.UpdatePicture(ID, CV.GetResultString());
				});
			}
		}

		private void Cam_Opened(object sender, EventArgs e)
		{
		}

		#endregion Camera

		#region Basic

		internal LLCVApi.LLCV CV = null;
		public FeedTray()
		{
		}
		public FeedTray(int id, string name, byte siteID)
		{
			ID = id; Name = name; _siteID = siteID;
			CV = GlobalParams.cves[id];

			LogUtility.LOG.Info(string.Format("FeedTray Created: Index={0} Name={1} SiteID={2}", id, Name, _siteID));
		}

		internal void InitCv()
		{
			CV.SetImageWHCh(Cam.Param.GetW(), Cam.Param.GetH(), 1);
			CV.SetCalibrationPts(GetCalibrationPts());
			CV.SetFlip(GetCfgFeed().Flip, GetCfgFeed().Trans);

			//var vp = GetVision(Index_Mark);
			//CV.SetROI(vp.roi_x, vp.roi_y, vp.roi_w, vp.roi_h);
			//CV.SetBackParams(vp.method, vp.binm, vp.binp);
			//CV.SetBackMarkFn(vp.mark_fn);			
			//CV.SetImageBack(Cam.Data);

		}

		private void SetCvParam(int mark)
		{
			var vp = GetVision(mark);
			//CV.SetCalibrationPts(GetCalibrationPts());
			CV.SetBackParams(vp.method, vp.binm, vp.binp);
			CV.SetBackMarkFn(vp.mark_fn);

			//CV.SetImageWHCh(Cam.Param.GetW(), Cam.Param.GetH(), 1);
			CV.SetROI(vp.roi_x, vp.roi_y, vp.roi_w, vp.roi_h);
			CV.SetFlip(GetCfgFeed().Flip, GetCfgFeed().Trans);

		}

		//private CfgFeedTray _cfg;
		internal WndConfigVision wndCfg = null;
		internal void UpdateConfig(CfgFeedTray cfg)
		{
			Name = cfg.Name;
			_siteID = cfg.SiteID;
			//_cfg = cfg;
		}
		internal CfgFeedTray GetCfgFeed()
		{
			return GlobalParams.CD.Trayes[ID];
		}

		internal List<float> GetCalibrationPts()
		{
			var calib = GetCfgFeed().calibrations;

			List<float> calib_xyuv = new List<float>();
			foreach (var f4 in calib)
			{
				calib_xyuv.Add(f4.X);
				calib_xyuv.Add(f4.Y);
				calib_xyuv.Add(f4.U);
				calib_xyuv.Add(f4.V);
			}
			return calib_xyuv;
		}

		internal void SetCalibrationTuple(List<float> calib_xyuv)
		{
			var calib = GetCfgFeed().calibrations;
			for (int i = 0; i < 4; i++)
			{
				calib[i].X = calib_xyuv[i * 4 + 0];
				calib[i].Y = calib_xyuv[i * 4 + 1];
				calib[i].U = calib_xyuv[i * 4 + 2];
				calib[i].V = calib_xyuv[i * 4 + 3];
			}
		}

		internal VibrationFeeder GetStep()
		{
			return GlobalParams.CR.Steps[ID];
		}

		internal PVisionParam GetVision(int idx)
		{
			if (idx == 0) return GlobalParams.CR.Steps[ID].vision1;
			else return GlobalParams.CR.Steps[ID].vision2;
		}

		public int ID = 0;
		public string Name = "FeedTray";
		private byte _siteID = 0;


		#endregion Basic

		#region thread_frame

		private TcpClient client = new TcpClient();
		public IModbusMaster master;
		public bool Connected => (client.Connected && master != null);

		private ThreadUtility.ThreadTimer _thread_timer = new ThreadUtility.ThreadTimer();
		internal void Init()
		{
			var cfg = GetCfgFeed();
			Name = cfg.Name;
			_siteID = cfg.SiteID;

			if (cfg.Enabled == false) return;
			
			Init_Cam(cfg.cfgCamera);
			InitCv();

			_thread_timer.Interval = cfg.TickMs;
			_thread_timer.Name = "_thread_timer_" + Name;
			_thread_timer.OnTick += _thread_timer_OnTick;
			_thread_timer.Enabled = true;
			_thread_timer.Start();
		}
		private Task Cam_task = null;

		DateTime dt = DateTime.Now;
		private void _thread_timer_OnTick()
		{
			if (GlobalParams.PC.Status_Verified.LO) return;
			if (!Cam.Connected)
			{
				if (Cam_task == null)
				{
					Cam_task = Task.Run(() =>
					{
						if (Cam.Open())
						{
							//CamUp.SetExposureTime(GlobalParams.CD.CameraUp.Exposure);
							//设置光源
							ReadLightMaxes();
							//CamUp.SetIOLevel(0);
							//GlobalParams.PC.PL.com.SetH(true);
							LogUtility.LOG.Info(Cam.Param.Name + ": OPENED");
						}
						else
						{
							MDVCamera.EnumerateCameras.Instance.EnumCameraName(Cam.Param.Name);
							System.Threading.Thread.Sleep(200);
						}
					});
				}
				else
				{
					if (Cam_task.Status != TaskStatus.Running) Cam_task = null;
				}
			}

			if (client.Connected)
			{
				ReadStatus();
				if (msShakeing.HI) processShakeing();
				else if (msSnaping.HI) processSnapping();
				else if (msBining.HI) processBining();
			}
			else
			{
				client = new TcpClient();
				var cfg = GetCfgFeed();
				try
				{
					client.Connect(cfg.IP, cfg.Port);
					master = ModbusIpMaster.CreateIp(client);
					master.Transport.ReadTimeout = 300;
					master.Transport.WriteTimeout = 300;
					master.Transport.Retries = 5;
					if (client.Connected)
					{
						ReadMotorFreqVTes();
						ReadBinVoltFreq();
					}
				}
				catch (Exception ex)
				{
					System.Diagnostics.Debug.WriteLine("Client.Connect" + ex.Message);
				}
			}
		}

		internal void ClosePhase2()
		{
			if (_thread_timer.Finished == false) _thread_timer.Abort();
			Cam.Close();
		}

		internal void ClosePhase1()
		{
			_thread_timer.Enabled = false;
			_thread_timer.RequiredExit();
			Cam.TriggerEnabled(false);
		}

		internal void Closed()
		{
			StopMoveCmd();
			LightSwitch(true, false);
			LightSwitch(false, false);
			BinSwitch(false);

			if (master != null) master.Dispose();
			if (client != null && client.Connected) client.Close();
			Cam.Close();
		}

		#endregion thread_frame

		#region read_write_Tray

		#region Feed_Status
		public bool S_FeedRuning = false;//平台运行
		public bool S_BinRuning = false;//料仓运行
		public bool S_LightA = false;
		public bool S_LightB = false;

		public int MoveMode = 0;
		public string Mode => ModeName(MoveMode);
		
		public string ModeName(int idx)
		{
			switch (idx)
			{
				case 0: return "上";
				case 1: return "下";
				case 2: return "左";
				case 3: return "右";
				case 4: return "左上";
				case 5: return "右上";
				case 6: return "左下";
				case 7: return "聚拢";
				case 8: return "右下";
				case 9: return "垂直居中";
				case 10: return "水平居中";
				case 11: return "振散";
				case 12: return "翻面";
				case 13: return "组合2";
				case 14: return "组合1";
				case 15: return "停止";
				default: return "停止";
			}
		}
		public bool S_FeedOver = false;//平台过流
		public bool S_BinOver = false;//料仓过流
		public bool S_Temperature = false;//系统过热
		public bool S_MotorLightTemperature = false;//电机或光源过热

		public bool S_LowVoltage = false;//系统欠压
		public bool S_HiVoltage = false;//系统过压
		private bool ReadStatus()
		{
			try
			{
				if (master == null || client.Connected == false) return false;

				var rtns = master.ReadHoldingRegisters(_siteID, 0x01, 1);
				if (rtns == null) return false;

				int us = rtns[0];

				S_FeedRuning = ((us >> 0) & 0x01) > 0;
				S_BinRuning = ((us >> 1) & 0x01) > 0;
				S_LightA = ((us >> 2) & 0x01) > 0;
				S_LightB = ((us >> 3) & 0x01) > 0;
				MoveMode = ((us >> 4) & 0x0F);
				S_FeedOver = ((us >> 8) & 0x01) > 0;
				S_BinOver = ((us >> 9) & 0x01) > 0;
				S_Temperature = ((us >> 10) & 0x01) > 0;
				S_MotorLightTemperature = ((us >> 11) & 0x01) > 0;
				S_LowVoltage = ((us >> 13) & 0x01) > 0;
				S_HiVoltage = ((us >> 14) & 0x01) > 0;

				return true;
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.WriteLine(Name + " " + ex.Message);
				return false;
			}
		}
		#endregion Feed_Status

		#region motor_fvt

		public ushort[] FreqVtes = new ushort[39];
		internal bool ReadMotorFreqVTes()
		{
			try
			{
				if (master == null || client.Connected == false) return false;

				var rtn = master.ReadHoldingRegisters(_siteID, 0x0010, 39);
				if (rtn == null) return false;

				FreqVtes = rtn;
				return true;
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.WriteLine(ex.Message);
				LogUtility.LOG.Error("ReadMotorFreqVTes", ex);
				return false;

			}
		}

		internal bool WriteMotorFreqVTes(ushort[] data)
		{
			try
			{
				if (master == null || client.Connected == false) return false;

				master.WriteMultipleRegisters(_siteID, 0x0010, data);
				return true;
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.WriteLine("ERROR WriteMotorFreqVTes " + ex.Message);
				LogUtility.LOG.Error("WriteMotorFreqVTes", ex);
				return false;

			}
		}

		#endregion motor_fvt

		#region light
		internal bool LightSwitch(bool A_B, bool onff)
		{
			if (A_B)
				return SendCmd(0x06, onff ? (ushort)0xFF00 : (ushort)0x00);
			else
				return SendCmd(0x07, onff ? (ushort)0xFF00 : (ushort)0x00);
		}

		internal bool LightLimit(bool a_b, ushort Value)
		{
			if (a_b)
				return SendCmd(0x52, Value);
			else
				return SendCmd(0x53, Value);
		}


		//internal bool[] LightOnes = new bool[2];
		//internal bool ReadLightONOFF()
		//{
		//	if (master == null || client.Connected == false) return false;
		//	var rtns = master.ReadHoldingRegisters(_siteID, 0x01, 1);
		//	if (rtns == null) return false;
		//	int us = rtns[0];

		//	LightOnes[0] = ((us >> 2) & 0x01) > 0;
		//	LightOnes[1] = ((us >> 3) & 0x01) > 0;

		//	return true;
		//}

		internal ushort[] LightMaxes =new ushort[2] { 6, 999 };
		internal bool ReadLightMaxes()
		{
			if (master == null || client.Connected == false) return false;
			LightMaxes = master.ReadHoldingRegisters(_siteID, (ushort)0X52, (ushort)2);
			if (LightMaxes == null) return false;
			return true;

		}

		internal ushort[] LightValues = new ushort[2];
		internal bool ReadLightValues()
		{
			if (master == null || client.Connected == false) return false;
			var us = master.ReadHoldingRegisters(_siteID, (ushort)0x50, (ushort)2);
			if (us == null) return false;

			LightValues[0] = us[0];
			LightValues[1] = us[1];
			return true;
		}

		internal bool Brightness6_999(bool a_b, ushort Value)
		{
			if (a_b)
				return SendCmd(0x50, Value);
			else
				return SendCmd(0x51, Value);
		}

		internal bool BrightnessPercent(bool a_b, ushort Value)
		{
			if (a_b)
			{
				ushort uv = Convert.ToUInt16((LightMaxes[0] - 6) * 0.01F * Value + 6);
				return SendCmd(0x50, uv);
			}
			else
			{
				ushort uv = Convert.ToUInt16((LightMaxes[1] - 6) * 0.01F * Value + 6);
				return SendCmd(0x51, uv);
			}
		}

		#endregion light

		#region bin
		internal bool BinSwitch(bool onff)
		{
			return SendCmd(0x05, onff ? (ushort)0xFF00 : (ushort)0x00);
		}
		internal bool WriteBinVoltFreq(ushort volt, ushort freq)
		{
			try
			{
				if (master == null || client.Connected == false) return false;

				master.WriteMultipleRegisters(_siteID, 0xc0, new ushort[2] { volt, freq });    //写入值到模拟输出寄存器，(从站地址、寄存器地址、写入值)
				return true;
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.WriteLine("ERROR WriteBinVoltFreq " + ex.Message);
				LogUtility.LOG.Error("WriteBinVoltFreq", ex);
				//MessageBox.Show(ex.Message);
				return false;
			}
		}

		internal ushort BinVolt = 0;
		internal ushort BinFreq = 0;
		internal bool ReadBinVoltFreq()
		{
			try
			{
				if (master == null || client.Connected == false) return false;
				var vf = master.ReadHoldingRegisters(_siteID, 0xc0, 2);
				if (vf == null) return false;
				BinVolt = vf[0];
				BinFreq = vf[1];
				return true;
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.WriteLine("ERROR ReadBinVoltFreq " + ex.Message);
				return false;
			}
		}
		#endregion bin

		internal bool ClearError()
		{
			return SendCmd(0x09, (ushort)0xFF00);
		}
		internal bool StopMoveCmd()
		{
			return WriteMoveMode(15);
		}
		internal bool WriteMoveMode(ushort cmd)
		{
			ushort temp = cmd;
			if (cmd != 15) temp = (ushort)(cmd | 0x10);
			return SendCmd(0x0F, temp);
		}
		private bool SendCmd(ushort address, ushort udata)
		{
			try
			{
				if (master == null || client.Connected == false) return false;

				master.WriteSingleRegister(_siteID, address, udata);    //写入值到模拟输出寄存器，(从站地址、寄存器地址、写入值)
				return true;
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.WriteLine("ERROR SendCmd " + ex.Message);
				LogUtility.LOG.Error("SendCmd", ex);
				return false;
			}

		}

		//internal bool ReadFVT(byte address, ref ushort freq, ref ushort vol, ref ushort tim)
		//{
		//	try
		//	{
		//		if (master == null || client.Connected == false) return false;

		//		var rtn = master.ReadHoldingRegisters(_siteID, address, 3);
		//		if (rtn == null) return false;
		//		freq = rtn[0];
		//		vol = rtn[1];
		//		tim = rtn[2];
		//		return true;
		//	}
		//	catch (Exception ex)
		//	{
		//		//MessageBox.Show(ex.Message);
		//		return false;

		//	}
		//}

		private bool WriteFVT(ushort address, ushort volt, ushort freq)
		{
			try
			{
				if (master == null || client.Connected == false) return false;

				master.WriteMultipleRegisters(_siteID, address, new ushort[2] { freq, volt });
				return true;
			}
			catch (Exception ex)
			{
				System.Diagnostics.Debug.WriteLine("ERROR WriteFVT " + ex.Message);
				LogUtility.LOG.Error("WriteFVT", ex);
				return false;
			}
		}

		#endregion read_write_Tray


		#region auto_runing

		#region Bining
		private MemoryState msBining = new MemoryState();
		FeedTraySteps<PShakeMove> bin_steps = new FeedTraySteps<PShakeMove>();
		internal bool StartBining()
		{
			CV.Clear();

			_status_message = CheckRuning();
			if (_status_message != "OK") return false;

			bin_steps.ClearMove(new List<PShakeMove>() { GetStep().boxparams });
			msBining.Update(true);
			msShakeing.Update(false);
			msSnaping.Update(false);

			return true;
		}

		private void processBining()
		{
			switch (bin_steps.Step)
			{
				case 0:
					if (bin_steps.GetNextMove())
					{
						bin_steps.Step = 10;
					}
					else
					{
						bin_steps.Step = 100;
					}
					break;

				case 10:
					GlobalParams.MF.wndRun.AddMessage(ID, "料仓打开");
					WriteBinVoltFreq(bin_steps.Current.Volt, bin_steps.Current.Freq);
					BinSwitch(true);
					bin_steps.DelayMove.Clear();
					bin_steps.Step = 12;
					break;

				case 12:
					if (bin_steps.DelayMove.Process(bin_steps.Current.Times))
					{
						if (bin_steps.Current.RestAfter > 0)
						{
							bin_steps.Step = 20;
							BinSwitch(false);
							bin_steps.DelayMove.Clear();
						}
						else
						{
							BinSwitch(false);
							bin_steps.Step = 0;
						}
						bin_steps.DelayMove.Clear();
					}
					break;
				case 20:
					if (bin_steps.DelayMove.Process(bin_steps.Current.RestAfter))
					{
						BinSwitch(false);
						bin_steps.Step = 0;
					}
					break;

				case 100:
					GlobalParams.MF.wndRun.AddMessage(ID, "料仓关闭");
					msBining.Update(false);
					if (BiningNeedResp)
					{
						SendResp("OK");
					}
					else if(AllInOne)
					{
						if (StartShakeing() == false)
						{
							SendResp(StatusMessage);
						}
					}
					break;
			}
		}
		#endregion Bining

		#region shakeing
		internal MemoryState msShakeing = new MemoryState();

		FeedTraySteps<PShakeMove> shake_steps = new FeedTraySteps<PShakeMove>();

		internal string StatusMessage => _status_message;
		private string _status_message = "OK";
		private string CheckRuning()
		{
			if (msShakeing.HI) return "震动盘工作中RUNING";
			if (msBining.HI) return "料仓开启中RUNING";
			if (msSnaping.HI) return "视觉处理中RUNING";
			return "OK";
		}

		internal MyNetwork.RVStatus Resp;
		internal bool ShakeNeedResp = false;
		internal bool BiningNeedResp = false;
		internal bool AllInOne = false;
		internal int CountShake = 0;
		internal int CountBin = 0;

		internal int Index_Mark1 = 0;
		internal int CountRequired1 = 0;
		internal int Index_Mark2 = 0;
		internal int CountRequired2 = 0;
		internal bool StartShakeing()
		{
			CV.Clear();

			_status_message = CheckRuning();
			if (_status_message != "OK") return false;

			shake_steps.ClearMove(GetStep().moves);
			msBining.Update(false);
			msSnaping.Update(false);
			lock(this) msShakeing.Update(true);
			return true;
		}
		private void processShakeing()
		{
			switch (shake_steps.Step)
			{
				case 0:
					GlobalParams.MF.wndRun.AddMessage(ID, "开始振动");
					shake_steps.Step = 1;
					break;
				case 1:
					if (shake_steps.GetNextMove())
					{
						shake_steps.Step = 10;
					}
					else
					{
						StopMoveCmd();
						shake_steps.Step = 100;
					}
					break;
				case 10:
					GlobalParams.MF.wndRun.AddMessage(ID, "振动:" + ModeName(shake_steps.Current.Mode) + " " + shake_steps.Current.GetMoveString());
					//GlobalParams.MF.wndRun.AddMessage(ID, "Address=" + ModeAddress(shake_steps.Current.Mode).ToString() + " V=" + shake_steps.Current.Volt.ToString() + " F=" + shake_steps.Current.Freq.ToString());
					WriteFVT(ModeAddress(shake_steps.Current.Mode), shake_steps.Current.Volt, shake_steps.Current.Freq);
					WriteMoveMode(shake_steps.Current.Mode);//move
					shake_steps.DelayMove.Clear();
					shake_steps.Step = 12;
					break;
				case 12:
					if (shake_steps.DelayMove.Process(shake_steps.Current.Times))
					{
						if (shake_steps.Current.RestAfter > 0)
						{
							shake_steps.Step = 20;
							StopMoveCmd();
							shake_steps.DelayMove.Clear();
						}
						else
						{
							shake_steps.Step = 1;
						}
						shake_steps.DelayMove.Clear();
					}
					break;

				case 20:
					if (shake_steps.DelayMove.Process(shake_steps.Current.RestAfter))
					{
						shake_steps.Step = 1;
					}
					break;

				case 100:
					GlobalParams.MF.wndRun.AddMessage(ID, "振动停止");
					msShakeing.Update(false);
					if (ShakeNeedResp)
					{
						SendResp("OK");
					}
					else if(AllInOne)
					{
						if (StartSnapping(true) == false)
						{
							SendResp(StatusMessage);
						}
					}					
					break;
			}
		}
		Dictionary<ushort, ushort> dic_mode_address = new Dictionary<ushort, ushort>()
		{
			{ 0,16 },
			{ 1,19 },
			{ 2,22 },
			{ 3,25 },
			{ 4,28 },
			{ 5,31 },
			{ 6,34 },
			{ 8,37 },
			{9,40 },
			{10,43 },
			{ 11,46 },
			{ 12,52 },
		};
		private ushort ModeAddress(ushort mode)
		{
			if (dic_mode_address.ContainsKey(mode)) return dic_mode_address[mode];
			return 49;
			//return (ushort)(0x10 + (3 * mode));
		}

		#endregion shakeing

		#region snaping
		internal MemoryState msSnaping = new MemoryState();

		class SnapParams
		{
			public PLightSnap snap = new PLightSnap();
			public int mark = 0;
			public int count_req = 0;
			public int cv_count_rtn = 0;
		}
		FeedTraySteps<SnapParams> snap_steps = new FeedTraySteps<SnapParams>();
		public string snap_error_msg = "";

		public List<int> cvDataMark = new List<int>();
		public List<LLCVApi.LLCV.ResultItem> cvData = new List<LLCVApi.LLCV.ResultItem>();

		private void sendSnap()
		{//send
			if (snap_steps.Items == null)
			{
				GlobalParams.CS.Send(Resp.RespCommand("NOT SUPPORT"));
				return;
			}
			string txt = "OK";
			if (cvData.Count == 0)
			{
				txt = "NO DATA";
			}
			else
			{
				lock (this)
				{
					for (int i = 0; i < cvData.Count && i < cvDataMark.Count; i++)
					{
						var cd = cvData[i];
						txt += "," + cd.GetSendDataString() + "," + cvDataMark[i].ToString();
					}
				}
			}
			GlobalParams.MF.wndRun.AddMessage(ID, "视觉: " + txt);
			GlobalParams.MF.wndRun.UpdatePicture(ID, txt);
			var stxt = Resp.RespCommand(txt);
			GlobalParams.CS.Send(stxt);
		}

		private void InitSnapSteps()
		{
			var snapes = new List<SnapParams>();
			if (GetStep().req_mode == 1)
			{
				if (GetVision(Index_Mark1).enabled)
				{
					var v = GetVision(Index_Mark1).snap;
					snapes.Add(new SnapParams() { snap = v, mark = Index_Mark1, count_req = CountRequired1 });
				}
				if (GetVision(Index_Mark2).enabled)
				{
					var v = GetVision(Index_Mark2).snap;
					snapes.Add(new SnapParams() { snap = v, mark = Index_Mark2, count_req = CountRequired2 });
				}
			}
			else
			{
				if (CountRequired1 > 0)
				{
					var v = GetVision(Index_Mark1).snap;
					snapes.Add(new SnapParams() { snap = v, mark = Index_Mark1, count_req = CountRequired1 });
				}

				if (CountRequired2 > 0)
				{
					var v = GetVision(Index_Mark2).snap;
					snapes.Add(new SnapParams() { snap = v, mark = Index_Mark2, count_req = CountRequired2 });
				}
			}
			snap_steps.ClearMove(snapes);
		}

		internal bool CheckData()
		{
			cvData.Clear();
			cvDataMark.Clear();

			int count = CV.GetCvData(Index_Mark1, cvData, CountRequired1);
			for (int i = 0; i < count; i++) cvDataMark.Add(Index_Mark1);
			if (count < CountRequired1) return false;

			count = CV.GetCvData(Index_Mark2, cvData, CountRequired2);
			for (int i = 0; i < count; i++) cvDataMark.Add(Index_Mark2);

			if (count < CountRequired2) return false;
			return true;
		}

		internal bool StartSnapping(bool force)
		{
			if (force == false)
			{
				if (GetStep().req_mode == 1)
				{
					if (CheckData())
					{
						sendSnap();
						return true;
					}
				}
			}

			lock (this)
			{
				cvData.Clear();
				cvDataMark.Clear();
			}
			CV.Clear();
			snap_error_msg = "";
			_status_message = CheckRuning();
			if (_status_message != "OK") return false;

			if (Cam.Connected == false)
			{
				_status_message = "Camera Not Connected.";
				return false;
			}

			InitSnapSteps();

			msShakeing.Update(false);
			msSnaping.Update(true);
			msBining.Update(false);
			return true;
		}
		private void processSnapping()
		{
			switch (snap_steps.Step)
			{
				case 0:
					Cam.TriggerEnabled(true);
					Cam.SetTriggerModeSoft();
					snap_steps.Step = 10;
					//System.Diagnostics.Debug.WriteLine("Snapping STEP=" + snap_steps.Step.ToString());
					break;
				case 10:

					if (snap_steps.GetNextMove())
					{
						SetCvParam(snap_steps.Current.mark);

						BrightnessPercent(true, snap_steps.Current.snap.BrightnessBack);
						BrightnessPercent(false, snap_steps.Current.snap.BrightnessFore);
						Cam.Exposure = snap_steps.Current.snap.Exposure;
						GlobalParams.MF.wndRun.AddMessage(ID, "曝光" + Cam.Exposure.ToString("F0")
							+ " B=" + snap_steps.Current.snap.BrightnessBack.ToString()
							+ " F=" + snap_steps.Current.snap.BrightnessFore.ToString());
						LightSwitch(true, true);
						LightSwitch(false, true);
						if (snap_steps.Current.snap.BrightnessBack == 0) LightSwitch(true, false);
						if (snap_steps.Current.snap.BrightnessFore == 0) LightSwitch(false, false);
						snap_steps.Step = 20;
						snap_steps.DelayMove.Clear();
						snap_steps.Result = false;
						snap_steps.Runing = true;
					}
					else
					{
						snap_steps.Step = 100;
					}
					//System.Diagnostics.Debug.WriteLine("Snapping STEP=" + snap_steps.Step.ToString());
					break;
				case 20:
					if (snap_steps.DelayMove.Process(50))
					{
						snap_steps.Step = 30;
					}
					//System.Diagnostics.Debug.WriteLine("Snapping STEP=" + snap_steps.Step.ToString());
					break;
				case 30:
					snap_steps.DelayMove.Clear();
					CV.proportion_workpieces = 0F;
					GlobalParams.MF.wndRun.AddMessage(ID, "开始拍照");
					Cam.Trigger();
					snap_steps.Step = 40;
					//System.Diagnostics.Debug.WriteLine("Snapping STEP=" + snap_steps.Step.ToString());
					break;
				case 40:
					if (snap_steps.Result)
					{
						int cmark = snap_steps.Current.mark;
						lock (this)
						{
							snap_steps.Current.cv_count_rtn = CV.GetCvData(cmark, cvData, snap_steps.Current.count_req);
							for (int i = 0; i < snap_steps.Current.cv_count_rtn; i++) cvDataMark.Add(cmark);
						}

						if (snap_steps.Current.cv_count_rtn < snap_steps.Current.count_req)
							snap_error_msg += "未找到Mark" + (snap_steps.Current.mark + 1).ToString() + " " + snap_steps.Current.count_req.ToString()
								+ " 实际找到" + snap_steps.Current.cv_count_rtn.ToString();
						snap_steps.Step = 10;
					}
					else if (snap_steps.DelayMove.Process(GlobalParams.CD.TimeoutCV))
					{
						snap_steps.Runing = false;
						snap_steps.Step = 50;//try again
					}
					break;

				case 50:
					snap_steps.DelayMove.Clear();
					snap_steps.Runing = true;
					GlobalParams.MF.wndRun.AddMessage(ID, "开始拍照2");
					Cam.Trigger();
					snap_steps.Step = 60;
					break;
				
				case 60:
					if (snap_steps.Result)
					{
						int cmark = snap_steps.Current.mark;
						lock (this)
						{
							snap_steps.Current.cv_count_rtn = CV.GetCvData(cmark, cvData, snap_steps.Current.count_req);
							for (int i = 0; i < snap_steps.Current.cv_count_rtn; i++) cvDataMark.Add(cmark);
						}

						if (snap_steps.Current.cv_count_rtn < snap_steps.Current.count_req)
							snap_error_msg += "未找到Mark" + (snap_steps.Current.mark + 1).ToString() + " " + snap_steps.Current.count_req.ToString()
								+ " 实际找到" + snap_steps.Current.cv_count_rtn.ToString();
						snap_steps.Step = 10;
					}
					else if (snap_steps.DelayMove.Process(GlobalParams.CD.TimeoutCV))
					{
						snap_steps.Runing = false;
						snap_steps.Step = -1;//try again
					}
					break;

				case 100:
					LightSwitch(true, false);
					LightSwitch(false, false);
					msSnaping.Update(false);
					snap_steps.Runing = false;
					if(AllInOne)
					{
						int count_t = CountRequired1 + CountRequired2;
						if (cvData.Count < count_t)
						{
							bool over_proportion = false;
							if (GetStep().Check_Proportion)
							{
								if (CV.proportion_workpieces > GetStep().Proportion_Limit)
								{
									over_proportion = true;
								}
							}

							if (over_proportion)
							{
								snap_error_msg = "工件占比过多 " + (CV.proportion_workpieces * 100F).ToString("F0") + "% > " +
									(GetStep().Proportion_Limit * 100F).ToString("F0") + "%";
								SendResp(snap_error_msg + " ERROR");
							}
							else
							{
								if (CountShake < GetStep().boxparams.TryCountShake)
								{
									CountShake++;
									StartShakeing();
								}
								else
								{
									if (CountBin < GetStep().boxparams.TryCountBin)
									{
										CountBin++;
										CountShake = 0;
										StartBining();
									}
									else
									{
										//error;
										SendResp(snap_error_msg + " ERROR");
									}
								}
							}
						}
						else
						{//send
							sendSnap();
						}
					}
					else
					{
						sendSnap();
					}

					break;

				case -1:
					msSnaping.Update(false);
					LightSwitch(true, false);
					LightSwitch(false, false);
					snap_steps.Runing = false;
					GlobalParams.MF.wndRun.AddMessage(ID, "视觉超时");
					SendResp("视觉超时");
					break;
			}
		}
		#endregion snaping


		private void SendResp(string emsg)
		{
			if (Resp != null)
			{
				GlobalParams.MF.wndRun.UpdatePicture(ID, emsg);

				string content = emsg + "," + Resp.Content;
				string txt = Resp.RespCommand(content);
				GlobalParams.CS.Send(txt);
			}
		}
		#endregion auto_runing
	}
}
